#include "stdafx.h"
#include "GUI.h"
#include "PropertyManager.hpp"
#include "Simulation.hpp"
#include "ArtificialSea.h"

template<> GUI* Ogre::Singleton<GUI>::ms_Singleton = 0;

using namespace TwOgre;

GUI::GUI(Ogre::RenderWindow* renderWindow, Ogre::SceneManager* sceneManager):
	renderWindow(renderWindow), sceneManager(sceneManager), windowManager(0), cursor(0)
{
	windowManager = new TwOgre::WindowManager(renderWindow, sceneManager);
	statWindow = windowManager->createWindow(Ogre::String("statWindow"), 
		Ogre::String("Simulation"), Ogre::ColourValue(1.0, 1.0, 1.0, 0.6));
	statWindow->setSize(250, 800);
	statWindow->setPosition(0, 0);

	cursor = new MouseCursor(renderWindow->getWidth(), renderWindow->getHeight());
	exitButton = statWindow->addButton(TwOgre::ButtonParams("exitButton")
        .label("Exit")
        .help("Exit Application")
		.callback(&GUI::exit, this)
        .shortcut("Alt+x"));
	fps = statWindow->addRealVariable("fps", true, "FPS");

	// ------------------------------------------------------------------------------------------------------------------------------------------------

	// Settings
	PM.paused = statWindow->addBoolVariable( BoolParams("paused").value(false) );
	PM.numFish = statWindow->addIntegerVariable( IntegerParams("numFish").label("num of fish").limits(0, 1000, 1).group("Settings") );
	PM.numPredator = statWindow->addIntegerVariable( IntegerParams("numPredator").label("num of predator").limits(0, 10, 1).group("Settings") );
	PM.forceScale = statWindow->addRealVariable( RealParams("forceScale").value(0).limits(0, 10000, 10).precision(0).group("Settings") );
	PM.damping = statWindow->addRealVariable( RealParams("damping").value(0).limits(0, 100, 0.1f).precision(1).group("Settings") );
	PM.zeroVelocity = statWindow->addRealVariable( RealParams("zeroVelocity").value(0).limits(0, 10, 0.01f).precision(2).group("Settings") );

	// ------------------------------------------------------------------------------------------------------------------------------------------------

	// Fish properties
	PM.fish.mass = statWindow->addRealVariable( RealParams("fish_mass").value(0).label("mass").limits(0.01f, 100, 0.05f).precision(2).group("Fish") );
	PM.fish.radius = statWindow->addRealVariable( RealParams("fish_radius").value(0).label("radius").limits(0.01f, 200, 0.1f).precision(1).group("Fish") );
	PM.fish.maxVelocity = statWindow->addRealVariable( RealParams("fish_maxVelocity").label("max speed").value(0).limits(0, 1000, 1).precision(0).group("Fish") );
	PM.fish.turnRate = statWindow->addRealVariable( RealParams("fish_turnRate").value(0).label("turn rate").limits(0, 100, 0.1f).precision(1).group("Fish") );

	// Fish/Weights
	PM.fish.weights.wander = statWindow->addRealVariable( RealParams("fish_weight_wander").value(0).label("wander").limits(0, 20, 0.01f).precision(2).group("Fish/Weights") );
	PM.fish.weights.separation = statWindow->addRealVariable( RealParams("fish_weight_separation").value(0).label("separation").limits(0, 20, 0.01f).precision(2).group("Fish/Weights") );
	PM.fish.weights.alignment = statWindow->addRealVariable( RealParams("fish_weight_alignment").value(0).label("alignment").limits(0, 20, 0.01f).precision(2).group("Fish/Weights") );
	PM.fish.weights.cohesion = statWindow->addRealVariable( RealParams("fish_weight_cohesion").value(0).label("cohesion").limits(0, 20, 0.01f).precision(2).group("Fish/Weights") );
	PM.fish.weights.evade = statWindow->addRealVariable( RealParams("fish_weight_evade").value(0).label("evade").limits(0, 20, 0.01f).precision(2).group("Fish/Weights") );

	// Fish/Wander
	PM.fish.wander.jitter = statWindow->addRealVariable( RealParams("fish_wander_jitter").value(0).label("jitter").limits(0, 1000, 0.1f).precision(1).group("Fish/Wander") );
	PM.fish.wander.distance = statWindow->addRealVariable( RealParams("fish_wander_distance").value(0).label("distance").limits(0, 1000, 0.1f).precision(1).group("Fish/Wander") );
	PM.fish.wander.radius = statWindow->addRealVariable( RealParams("fish_wander_radius").value(0).label("radius").limits(0, 1000, 0.1f).precision(1).group("Fish/Wander") );

	// Fish/Flocking
	PM.fish.flocking.neighborRadius = statWindow->addRealVariable( RealParams("fish_flocking_neighborRadius").value(0).label("neighborRadius").limits(0, 500, 0.1f).precision(1).group("Fish/Flocking") );

	// Fish/Separation
	PM.fish.separation.radius = statWindow->addRealVariable( RealParams("fish_separation_radius").value(0).label("radius").limits(0, 500, 0.1f).precision(1).group("Fish/Separation") );

	// Fish/Evade
	PM.fish.evade.dangerRadius = statWindow->addRealVariable( RealParams("fish_evade_dangerRadius").value(0).label("dangerRadius").limits(0, 500, 0.1f).precision(1).group("Fish/Evade") );
	PM.fish.evade.turnAround = statWindow->addRealVariable( RealParams("fish_evade_turnAround").value(0).label("turnAround").limits(0, 50, 0.01f).precision(2).group("Fish/Evade") );
	PM.fish.evade.timeScale = statWindow->addRealVariable( RealParams("fish_evade_timeScale").value(0).label("timeScale").limits(0, 100, 0.1f).precision(1).group("Fish/Evade") );

	// ------------------------------------------------------------------------------------------------------------------------------------------------

	// Predator properties
	PM.predator.mass = statWindow->addRealVariable( RealParams("predator_mass").value(0).label("mass").limits(0.01f, 100, 0.05f).precision(2).group("Predator") );
	PM.predator.radius = statWindow->addRealVariable( RealParams("predator_radius").value(0).label("radius").limits(0.01f, 200, 0.1f).precision(1).group("Predator") );
	PM.predator.maxVelocity = statWindow->addRealVariable( RealParams("predator_maxVelocity").label("max speed").value(0).limits(0, 1000, 1).precision(0).group("Predator") );
	PM.predator.turnRate = statWindow->addRealVariable( RealParams("predator_turnRate").value(0).label("turn rate").limits(0, 100, 0.1f).precision(1).group("Predator") );

	// Predator/Wander
	PM.predator.wander.jitter = statWindow->addRealVariable( RealParams("predator_wander_jitter").value(0).label("jitter").limits(0, 1000, 0.1f).precision(1).group("Predator/Wander") );
	PM.predator.wander.distance = statWindow->addRealVariable( RealParams("predator_wander_distance").value(0).label("distance").limits(0, 1000, 0.1f).precision(1).group("Predator/Wander") );
	PM.predator.wander.radius = statWindow->addRealVariable( RealParams("predator_wander_radius").value(0).label("radius").limits(0, 1000, 0.1f).precision(1).group("Predator/Wander") );

	// Predator/Pursuit
	PM.predator.pursuit.attackRadius = statWindow->addRealVariable( RealParams("predator_pursuit_attackRadius").value(0).label("attackRadius").limits(0, 500, 0.1f).precision(1).group("Predator/Pursuit") );
	PM.predator.pursuit.timeScale = statWindow->addRealVariable( RealParams("predator_pursuit_timeScale").value(0).label("timeScale").limits(0, 100, 0.1f).precision(1).group("Predator/Pursuit") );

	// ------------------------------------------------------------------------------------------------------------------------------------------------

	// Diver properties
	PM.diver.mass = statWindow->addRealVariable( RealParams("diver_mass").value(0).label("mass").limits(0, 100, 0.05f).precision(2).group("Diver") );
	PM.diver.radius = statWindow->addRealVariable( RealParams("diver_radius").value(0).label("radius").limits(0, 200, 0.1f).precision(1).group("Diver") );
	PM.diver.maxVelocity = statWindow->addRealVariable( RealParams("diver_maxVelocity").label("max speed").value(0).limits(0, 1000, 1).precision(0).group("Diver") );
	PM.diver.turnRate = statWindow->addRealVariable( RealParams("diver_turnRate").value(0).label("turn rate").limits(0, 100, 0.1f).precision(1).group("Diver") );

	// Diver/Ranges
	PM.diver.ranges.separation = statWindow->addRealVariable( RealParams("diver_ranges_separation").value(0).label("separation").limits(0, 1000, 0.1f).precision(1).group("Diver/Ranges") );
	PM.diver.ranges.evasion = statWindow->addRealVariable( RealParams("diver_ranges_evasion").value(0).label("evasion").limits(0, 1000, 0.1f).precision(1).group("Diver/Ranges") );

	// ------------------------------------------------------------------------------------------------------------------------------------------------

	PM.paused->setBoolValue(false);
	PM.numFish->setIntegerValue(230);
	PM.numPredator->setIntegerValue(2);

	PM.forceScale->setRealValue(2000.0f);
	PM.damping->setRealValue(1.0f);
	PM.zeroVelocity->setRealValue(1.0f);

	PM.fish.mass->setRealValue(1.0f);
	PM.fish.radius->setRealValue(2.0f);
	PM.fish.maxVelocity->setRealValue(30.0f);
	PM.fish.turnRate->setRealValue(2.0f);

	PM.fish.weights.wander->setRealValue(9.0f);
	PM.fish.weights.separation->setRealValue(9.75f);
	PM.fish.weights.alignment->setRealValue(2.0f);
	PM.fish.weights.cohesion->setRealValue(7.3f);
	PM.fish.weights.evade->setRealValue(4.0f);

	PM.fish.wander.jitter->setRealValue(235.0f);
	PM.fish.wander.distance->setRealValue(80.0f);
	PM.fish.wander.radius->setRealValue(23.0f); //44
	
	PM.fish.flocking.neighborRadius->setRealValue(36.0f);

	PM.fish.separation.radius->setRealValue(10.0f);

	PM.fish.evade.dangerRadius->setRealValue(60.0f);
	PM.fish.evade.turnAround->setRealValue(0.0f);
	PM.fish.evade.timeScale->setRealValue(0.5f);

	// -------------------------------------

	PM.predator.mass->setRealValue(2.0f);
	PM.predator.radius->setRealValue(12.0f);
	PM.predator.maxVelocity->setRealValue(28.0f);
	PM.predator.turnRate->setRealValue(0.8f);

	PM.predator.wander.jitter->setRealValue(60.0f);
	PM.predator.wander.distance->setRealValue(100.0f);
	PM.predator.wander.radius->setRealValue(20.0f);

	PM.predator.pursuit.attackRadius->setRealValue(110.0f);
	PM.predator.pursuit.timeScale->setRealValue(1.0f);

	// -------------------------------------

	PM.diver.mass->setRealValue(3.0f);
	PM.diver.radius->setRealValue(15.0f);
	PM.diver.maxVelocity->setRealValue(40.0f);
	PM.diver.turnRate->setRealValue(10.0f);

	PM.diver.ranges.separation->setRealValue(30.0f);
	PM.diver.ranges.evasion->setRealValue(0.0f);
}

GUI::~GUI()
{
	delete windowManager;
}

void GUI::update()
{
	fps->setRealValue(renderWindow->getAverageFPS());
}

void TW_CALL GUI::exit(void* clientData) 
{
	ArtificialSea::getSingleton().setShutDown(true);
}